// Masyu.cpp : Defines the entry point for the console application.
//
//#include "Cell.h"
#include "Masyu3.h"
#include <boost/thread.hpp>

//#include <iostream>
//using namespace std;

int main(int argc, char *argv[])
{
	if (argc != 2) 
	{
		cout<<"Args != 2!"<<endl;
		//return 1;
	}
	
	//cout<<argv[1]<<endl;
	
	cout<<"MAIN FUNCTION WORKING!!!\n";
/*	std::cout << "main: startup" << std::endl;

	boost::thread workerThread(stupid_function);

	std::cout << "main: waiting for thread" << std::endl;

	workerThread.join();

	std::cout << "main: done" << std::endl;
	*/
	//stateManage(argv[1]);
	if (1 == stateManage("input.txt")) return 1;	//this is a debug stub
	
	   
	//textReader(argv[1]);	  
	return 0;
}
//textReader reads only one string for input of white and black cells
int textReader(const char *argg, list<Cell*>& blackCells, list<Cell*>& whiteCells, Cell **myarr)
{
	cout<<"TEXT READER WORKING!!!\n";
	ifstream fin(argg);
	stringstream iss;
	string seg, strg;
	int ii;                 //iterator used for rows in myarr
	int zz;					//iter used for columns
	if (!fin)
	{
		cout<<"sorry! Ne fin :)"<<endl;
		return 1;
	}
	while (!fin.eof())
	{
		//cout<<"MARK1"<<endl;
		getline(fin, strg);
		if (strg == "B")
		{
			getline(fin, strg);
			iss.str(strg);
			while (iss>>ii)
			{
				iss>>zz;
				--ii;--zz;    //we decrement them to fit into array of m size. we have to increment them before final output
				//cout<<"MARK2"<<endl;
				myarr[ii][zz].setPos(ii, zz);
				//cout<<"MARK21"<<endl;
				blackCells.push_back(&myarr[ii][zz]);
				//cout<<"MARK22"<<endl;
			}
		}
		if (strg == "W")
		{
			getline(fin, seg);
			iss.str(seg);
			iss.clear();

			while (iss>>ii)
			{
				iss>>zz;
				--ii;--zz;
				//cout<<"MARK3"<<endl;
				myarr[ii][zz].setPos(ii, zz);
				whiteCells.push_back(&myarr[ii][zz]);
			}
		}
	}
	//cout<<"MARK4"<<endl;
	cout<<"Coordinates of black cells: \n";
	for (iter ia = blackCells.begin(); ia != blackCells.end(); ++ia)
	{
		//cout<<"MARK5"<<endl;
		cout<<*ia;
	}
	cout<<"\nCoordinates of white cells: \n";
	for (iter ia = whiteCells.begin(); ia != whiteCells.end(); ++ia)
	{
		//cout<<"MARK6"<<endl;
		cout<<*ia;
	}
	//cout<<"Here is blacklist INSIDE textreader (5,0): "<<blackCells.front()<<endl;
	return 0;
}

int stateManage(const char *argg)
{
	cout<<"STATE MANAGE WORKING!!!\n";
	Cell **myarr;
	const int M = 7;					// this magic number makes me think of it.
	const int N = M - 1; 
	myarr = new Cell *[M];				//we create a 2-dimension array. Can we use not default constructor here to initialize pos immediately?
	for (int j = 0; j < M; j++)
	{
		//cout<<"Starting to create "<<j<<" elements..."<<endl;
		myarr[j] = new Cell[M];
	}
	//Initialising coordinates
	for (int i = 0; i < M; i++)
	{
		for (int j = 0; j < M; j++)
		{
			//cout<<"Starting to create "<<j<<" elements..."<<endl;
			myarr[i][j].setPos(i,j);
		}
	}
	//////////////////////////////////////////////////////////////////////////
	multimap<int*, Cell*> objfinder;
	if (1 == initMultiset(myarr, N, objfinder)) return 1;		//make sure we send SIZE-1

	//DEBUG: checking we have different addresses
	/*
	for (int i = 0; i < M; i++)
	{
		for (int j = 0; j < M; j++)
		{
			cout<<"myarr["<<i<<"]["<<j<<"] = "<<&myarr[i][j];
		}
	}
	*/
	/*
	cout<<"myarr["<<1<<"]["<<0<<"] = "<<&myarr[1][0];
	cout<<"myarr["<<3<<"]["<<0<<"] = "<<&myarr[3][0];
	cout<<"myarr["<<6<<"]["<<2<<"] = "<<&myarr[6][2];
	cout<<"myarr["<<6<<"]["<<4<<"] = "<<&myarr[6][4];
	cout<<"myarr["<<6<<"]["<<3<<"] = "<<&myarr[6][3];
	cout<<"myarr["<<6<<"]["<<5<<"] = "<<&myarr[6][5];
	cout<<"myarr["<<3<<"]["<<6<<"] = "<<&myarr[3][6];
	cout<<"myarr["<<5<<"]["<<6<<"] = "<<&myarr[5][6];

	cout<<"\nmyarr["<<2<<"]["<<1<<"] = "<<&myarr[2][1];
	cout<<"myarr["<<5<<"]["<<3<<"] = "<<&myarr[5][3];
	cout<<"myarr["<<5<<"]["<<4<<"] = "<<&myarr[5][4];
	cout<<"myarr["<<4<<"]["<<5<<"] = "<<&myarr[4][5];
	
	cout<<"\nmyarr["<<2<<"]["<<0<<"] = "<<&myarr[2][0]<<" "<<myarr[2][0].getCondition()<<endl;
	cout<<"myarr["<<6<<"]["<<3<<"] = "<<&myarr[6][3]<<" "<<myarr[6][3].getCondition()<<endl;
	cout<<"myarr["<<6<<"]["<<4<<"] = "<<&myarr[6][4]<<" "<<myarr[6][4].getCondition()<<endl;
	cout<<"myarr["<<4<<"]["<<6<<"] = "<<&myarr[4][6]<<" "<<myarr[4][6].getCondition()<<endl;
	*/
	list<Cell*> blackCells, whiteCells;
	if (1 == textReader(argg, blackCells, whiteCells, myarr)) return 1;			//after doing it we got lists of objects of black and white cells
	cout<<"WE ARE BACK IN STATE MANAGER"<<endl;
	
	if (1 == whiteProcessing(whiteCells, objfinder)) return 1;
	if (1 == blackProcessing(blackCells, objfinder)) return 1;
	
	//DEBUG: CHECKING correctness of setting white cells' borders to blocked
	/*
	for (int i = 0; i<4; i++)
	{
		cout<<"Directions of myarr["<<2<<"]["<<0<<"] = "<<*myarr[2][0].getDirection().borders[i]<<endl;
	}
	for (int i = 0; i<4; i++)
	{
		cout<<"Directions of myarr["<<6<<"]["<<3<<"] = "<<*myarr[6][3].getDirection().borders[i]<<endl;
	}
	for (int i = 0; i<4; i++)
	{
		cout<<"Directions of myarr["<<6<<"]["<<4<<"] = "<<*myarr[6][4].getDirection().borders[i]<<endl;
	}
	for (int i = 0; i<4; i++)
	{
		cout<<"Directions of myarr["<<4<<"]["<<6<<"] = "<<*myarr[4][6].getDirection().borders[i]<<endl;
	}
	*/
	
	for (int i = 0; i < M; i++) {
		delete []myarr[i];
	}
	delete []myarr;

	//DEBUG
	/*
	//THIS BLOCK IS TO CHECK IF LISTS ARE FILLED CORRECTLY
	//OBSOLETE needs overloading of operator<<
	typedef list<Cell>::iterator iter;
	cout<<"Coordinates of black cells: \n";
	for (iter ia = blackCells.begin(); ia != blackCells.end(); ++ia)
	{

		cout<<*ia;
		//cout<<"x and y coordinates: "<<ia->getPos();
	}
	cout<<"\nCoordinates of white cells: \n";
	for (iter ia = whiteCells.begin(); ia != whiteCells.end(); ++ia)
	{

		cout<<*ia;
		//cout<<"x and y coordinates: "<<ia->getPos();
	}
	*/
	return 0;
}

int blackProcessing(list<Cell *> &blCells, multimap<int*, Cell*> objcontainer)
{
	Cell* tempCell;
	pair<int, int> res;
	pair<Cell*, Cell*> mapres;		//for keeping addresses of neighbors returned from getMapObject
	
	for (iter it = blCells.begin(); it != blCells.end(); it++)
	{
		tempCell = *it;
		//cout<<"\nblackCells pings:"<<endl;
		for (int i = UP; i <= LEFT; i += i)
		{
			if (1 == pingDirection(i, tempCell, objcontainer))
			{
				cout<<"Ping direction started"<<endl;

				tempCell->setDirectionTo(make_pair(i, 0), BLOCKED);		//block the wrong direction
				res = tempCell->checkDirectionSwitch(BLACK);			//get the right direction
				tempCell->setDirectionTo(res, FREE);					//set it to free
				
				cout<<"We ve just set this direction to FREE:\n"<<res.first<<endl;
				cout<<"Current cell: \n"<<tempCell->getPos().first<<" "<<tempCell->getPos().second<<endl;
				cout<<"LETS CHECK the condition!!\n"<<tempCell->getCondition()<<endl;
				
				mapres = getMapObject(objcontainer, tempCell, res);		//get the neighbor in the right direction
				tempCell = mapres.first;
				tempCell->setCondition(tempCell->getDirection().);
				res = tempCell->checkDirectionSwitch(BLACK);			//get the same direction of the neighbor
				tempCell->setDirectionTo(res, FREE);					//set it to FREE
				
				cout<<"We ve just set this direction to FREE:\n"<<res.first<<endl;
				cout<<"Current cell: \n"<<tempCell->getPos().first<<" "<<tempCell->getPos().second<<endl;
				cout<<"tempCell->getDirection().num_free\n"<<tempCell->getDirection().num_free<<endl;
				
				mapres = getMapObject(objcontainer, tempCell, res);

				/*now 
				in condition we process the case to prevent overincrementing of condition if */
				/*blocked and free are set, but it still means the same condition as they are orthogonal*/
				cout<<"Current cell: \n"<<mapres.first->getPos().first<<" "<<mapres.first->getPos().second<<endl;
				cout<<"mapres.first->getDirection().num_free\n"<<mapres.first->getDirection().num_free<<endl;
				if (mapres.first->getCondition() != READY \
					&& ((logar(mapres.first->getDirection().num_free) < 100  \
					&& logar(mapres.first->getDirection().num_free) > 0      \
					&& logar(mapres.first->getDirection().num_blocked) < 100 \
					&& logar(mapres.first->getDirection().num_blocked) > 0)  \
					|| (logar(mapres.first->getDirection().num_free) == 0    \
					&& logar(mapres.first->getDirection().num_blocked) == 0)))

				{
					mapres.first->setCondition(mapres.first->getCondition()+2);		//tricky logic here.Instead of sending to setCondition num_blocked,
				}															//we send values == either 2 or 3, which sets condition to HALF/READY respectively




				if (READY == tempCell->getCondition())
				{
					res = tempCell->checkDirectionSwitch(BLACK);
					tempCell->setDirectionTo(res, BLOCKED);
				}
				else
				{
					cout<<"LETS return 1 because I don't know what should happen here"<<endl;
					return 1;
				}

			}
			
		}
		
	}

	return 0;
}


int whiteProcessing(list<Cell *> &whCells, multimap<int*, Cell*> objcontainer)
{
	Cell* tempCell;
	pair<int, int> res;
	pair<Cell*, Cell*> mapres;		//for keeping addresses of neighbors returned from getMapObject
	//pair<int, int> coordinates;
	for (iter it = whCells.begin(); it != whCells.end(); it++)
	{
		
		tempCell = *it;
		//coordinates = tempCell->getPos();
		res = tempCell->checkDirectionSwitch(WHITE);
		if (1 == res.second) return 1;
		if (res.first != 0)		//It equals to condition != NOT
		{
			if (HALF == tempCell->getCondition())
			{
				//cout<<"whiteProcessing: HALF == getCondition"<<endl;
				if (tempCell->getDirection().num_blocked != 0) tempCell->setDirectionTo(res, BLOCKED);
				else if (tempCell->getDirection().num_free != 0) tempCell->setDirectionTo(res, FREE);
				else 
				{
					cout<<"Crap! HALF == tempCell->getCondition() returned crap!\n";
					return 1;
				}
				
			}
			
			if (READY == tempCell->getCondition())
			{
				//cout<<"whiteProcessing: READY == getCondition"<<endl;
				
				if (0 == tempCell->getDirection().num_blocked && tempCell->getDirection().num_free != 0)	//check that no blocked but there are free
				{
					res = tempCell->checkDirectionSwitch(WHITE);			//if so, we want to get borders to block, cuz there have to be blocked borders
					tempCell->setDirectionTo(res, BLOCKED);
				}
				if (tempCell->getDirection().num_blocked != 0) res = tempCell->checkDirectionSwitch(WHITE);		//so we got them and we get ...
																												//...FREE now to set condition to neighbors
				
				mapres = getMapObject(objcontainer, tempCell, res);
				
				tempCell = mapres.first;
				/*now in condition we process the case to prevent overincrementing of condition if */
				/*blocked and free are set, but it still means the same condition as they are orthogonal*/
				if (tempCell->getCondition() != READY \
					&& logar(tempCell->getDirection().num_blocked) < 100 \
					&& logar(tempCell->getDirection().num_blocked) > 0 \
					&& logar(tempCell->getDirection().num_free) < 100  \
					&& logar(tempCell->getDirection().num_free) > 0)
				{
					tempCell->setCondition(tempCell->getCondition()+2);		//tricky logic here.Instead of sending to setCondition num_blocked,
				}															//we send values == either 2 or 3, which sets condition to HALF/READY respectively
				tempCell = mapres.second;
				if (tempCell->getCondition() != READY \
					&& logar(tempCell->getDirection().num_blocked) < 100 \
					&& logar(tempCell->getDirection().num_blocked) > 0 \
					&& logar(tempCell->getDirection().num_free) < 100  \
					&& logar(tempCell->getDirection().num_free) > 0)
				{
					tempCell->setCondition(tempCell->getCondition()+2);		//tricky logic here.Instead of sending to setCondition num_blocked,
				}
				cout<<"getMapobject result: \n"<<mapres.first<<endl<<mapres.second<<endl;
				if (it != whCells.begin())
				{
					//cout<<"\nElement to erase: "<<*it<<endl;
					it = whCells.erase(it);
					it--;
					//cout<<"Element pointed to after erasing: "<<*it<<endl;
										
				}
				else
				{
					it = whCells.erase(it);		//NOT TESTED!! IF the first element in the list has to be removed
				}
				
			}
			else
			{
				cout<<"Looks like some shit happened here!!!"<<endl;
				return 1;
			}
		}
		/*
		cout<<"This is s list of white cells after erasing elements"<<endl;
		for (iter ia = whCells.begin(); ia != whCells.end(); ++ia)
		{
			//cout<<"MARK6"<<endl;
			cout<<*ia;
		}
		*/
		

	}
	return 0;
}


int initMultiset(Cell **cellarr, const int N, multimap<int*, Cell*>& objcontainer)
{
	for (int i = 0; i <= N; i++)							//this sets shared borders between cells and status "blocked" to edge cells' borders
	{
		for (int j = 0; j <= N; j++)
		{
			if (j != 0)
			{
				if (j == N)
				{
					if (1 == cellarr[i][j].setDirectionTo(make_pair(DOWN, 0), BLOCKED)) return 1;		//sets bottom edge to block
				}
				delete cellarr[i][j].getDirection().borders[logar(UP)];
				cellarr[i][j].copyDirection(UP, cellarr[i][j-1].getDirection().borders[logar(DOWN)]);	//sets shared horizontal borders
			} else
			{
				if (1 == cellarr[i][j].setDirectionTo(make_pair(UP, 0), BLOCKED)) return 1;				//sets upper edge to blocked
			}

			if (i != 0)
			{
				if (i == N) 
				{
					if (1 == cellarr[i][j].setDirectionTo(make_pair(RIGHT, 0), BLOCKED)) return 1;		//sets right edge to blocked
				}
				delete cellarr[i][j].getDirection().borders[logar(LEFT)];
				cellarr[i][j].copyDirection(LEFT, cellarr[i-1][j].getDirection().borders[logar(RIGHT)]);		//sets vertical shared borders
			} else
			{
				if (1 == cellarr[i][j].setDirectionTo(make_pair(LEFT, 0), BLOCKED)) return 1;					//sets left edge to blocked
			}
		}
	}
	

	
	for (int i = 0; i <= N; i++)
	{
		for (int j = 0; j <= N; j++)
		{
			objcontainer.insert(make_pair(cellarr[i][j].getDirection().borders[logar(DOWN)], &cellarr[i][j])); 	//we place direction fields' pointers of array object into map as a key, address of array object as a value
			objcontainer.insert(make_pair(cellarr[i][j].getDirection().borders[logar(UP)], &cellarr[i][j])); 
			objcontainer.insert(make_pair(cellarr[i][j].getDirection().borders[logar(LEFT)], &cellarr[i][j])); 
			objcontainer.insert(make_pair(cellarr[i][j].getDirection().borders[logar(RIGHT)], &cellarr[i][j])); 
		}
	}
	//DEBUG
	
	cout<<"Here is what multimap says:"<<endl;
	cout<<objcontainer.find(cellarr[6][0].getDirection().borders[logar(LEFT)])->first<<endl;
	cout<<objcontainer.find(cellarr[5][0].getDirection().borders[logar(RIGHT)])->first<<endl;
	cout<<objcontainer.find(cellarr[6][1].getDirection().borders[logar(UP)])->first<<endl;
	cout<<objcontainer.find(cellarr[6][0].getDirection().borders[logar(DOWN)])->first<<endl;
	

	return 0;
}


pair<Cell*, Cell*> getMapObject(multimap<int*, Cell*> objcontainer, Cell *tempCell, pair<int, int> bordrs)	//I don't like to pass objcomntainer by ref !!!!!
{
	pair<Cell*, Cell*> pair_of_neighbor_cells;					//Here is a pair of pointers to Cell objects which we need to return
	typedef multimap<int*, Cell*>::const_iterator Itor;
	pair<Itor, Itor> p = objcontainer.equal_range(tempCell->getDirection().borders[logar(bordrs.first)]);	//Lets get range of pointers to needed object
	
	//DEBUG
	/*
	Itor itt = p.first;
	cout<<"getMapObject equal range FIRST:\n"<<itt->second<<endl;
	++itt;
	cout<<"++itt\n";
	cout<<"getMapObject equal range SECOND:\n"<<itt->second<<endl;
	*/
	for (Itor it = p.first; it != p.second; ++it)		//while it doesn't equal to a different value
	{
		if (it->second != tempCell) pair_of_neighbor_cells.first = it->second;	//if it doesn't point to THIS object, we got the pointer to an object we need

	}
	if (bordrs.second != 0)		//to check if we want to find 2 neighbors
	{
		p = objcontainer.equal_range(tempCell->getDirection().borders[logar(bordrs.second)]);	//searching for a second neighbor
		for (Itor it = p.first; it != p.second; ++it)
		{
			if (it->second != tempCell) pair_of_neighbor_cells.second = it->second;
		}
	}
	else pair_of_neighbor_cells.second = NULL;
	
	
	return pair_of_neighbor_cells;
}

int logar(int dir)
{
	//int res = (int)(log10((float)dir)/log10((float)2.0));
	switch (dir)
	{
	case 1:
		return 0;
	case 2:
		return 1;
	case 4:
		return 2;
	case 8:
		return 3;
	default:
		return 100;		//it's gonna crash receiving this value, i guess))
	}
	
}


int pingDirection(int dir, Cell* tmpCell, multimap<int*, Cell*> objcontainer)
{
	pair<Cell*, Cell*> mapres;
	//cout<<"*tmpCell->getDirection().borders[dir]"<<endl;
	//cout<<*tmpCell->getDirection().borders[logar(dir)]<<endl;
	if (*tmpCell->getDirection().borders[logar(dir)] != BLOCKED)
	{
		mapres = getMapObject(objcontainer, tmpCell, make_pair(dir, 0));
		tmpCell = mapres.first;
		if (*tmpCell->getDirection().borders[logar(dir)] != BLOCKED) return 0;
	}
	return 1;	//Not an error. Just this direction is blocked! no error check! Be careful.
}